home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / cli / UnixUtils.lha / UnixUtils / Source / tree.c < prev   
Encoding:
C/C++ Source or Header  |  1992-04-05  |  16.5 KB  |  625 lines

  1. /*-------------------------------------------------------------*
  2.  | File: TREE.c - After Tomas Rokicki, Fred Fish Disk nr. 306  |
  3.  | Revised: Maurizio LORETI (MLO) - First version 1.00, 911123 |
  4.  | Last revision: version 1.04, MLO 920405                     |
  5.  +-------------------------------------------------------------+
  6.  | For the description, refer to the README file, or to the    |
  7.  | Usage() procedure at the end of this program; a short help  |
  8.  | will be printed giving the command TREE ? , TREE -H or TREE |
  9.  | followed by an invalid option.                              |
  10.  *-------------------------------------------------------------*/
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <exec/types.h>
  16. #include <exec/exec.h>
  17. #include <libraries/dos.h>
  18. #include <proto/exec.h>
  19. #include <proto/dos.h>
  20. #include "pattern.h"
  21.  
  22. #define SYS_NORMAL_CODE    0     /* Exit codes for the operating system */
  23. #define SYS_ABORT_CODE     1
  24.  
  25. #define MATCH_STRING       -1    /* Flag for the NamEntry tree */
  26.  
  27. #define FILENAME_MAX       128   /* Dimension of the file name buffer */
  28. #define OUTLINE_DIM        128   /* Dimension of the printout buffer*/
  29.  
  30. /**
  31.  | Structures to implement: a linked list of character strings
  32.  | (StringSet); a linked list of directory entries (DirEntry);
  33.  | and a linked list of name patterns (NamEntry).
  34. **/
  35.  
  36. typedef struct sStringSet {
  37.    struct sStringSet *next;
  38.    char what[1];
  39. } StringSet;
  40.  
  41. typedef struct sDirEntry {
  42.    struct sDirEntry *next;
  43.    char name[1];
  44. } DirEntry;
  45.  
  46. typedef struct sNamEntry {
  47.    struct sNamEntry *next;
  48.    LONG type;
  49.    void *pArg;
  50. } NamEntry;
  51.  
  52. /**
  53.  | Global variables
  54. **/
  55.  
  56. struct Library *PatternBase = NULL;       /* pattern.library pointer */
  57.  
  58. struct FileInfoBlock *pFIB = NULL;        /* FileInfoBlock buffer */
  59. int aborted                = FALSE;       /* CTRL-C flag */
  60. int dirWanted              = FALSE;       /* "-x" or "+x" flag */
  61. int namWanted              = FALSE;       /* "-n|N" or "+n|N" flag */
  62. int caseSignificant;                      /* "case is significant" flag */
  63. int fullDirPath;                          /* "-d" or "-D" flag */
  64. StringSet *dirList         = NULL;        /* Subdirectories tree root */
  65. StringSet *extList         = NULL;        /* Extensions tree root */
  66. NamEntry  *namList         = NULL;        /* Name patterns tree root */
  67. char *mFormat              = "%p%s";      /* Default output format */
  68. char outLine[OUTLINE_DIM];                /* Output print buffer */
  69.  
  70. /**
  71.  | Procedure prototypes
  72. **/
  73.  
  74. StringSet  *Add(char *s, StringSet *pSS);
  75. NamEntry   *AddNam(char *s, NamEntry *pNE);
  76. void        CheckControlC(void);
  77. int         CheckExt(char *s);
  78. void        Cleanup(int code);
  79. int         CXBRK(void);
  80. int         FileDate(char *pc, struct DateStamp *pDS);
  81. int         FileTime(char *pc, struct DateStamp *pDS);
  82. int         Member(char *s, StringSet *pSS);
  83. int         NamMatch(char *name);
  84. void        NoMem(void);
  85. void        PrintFile(char *s);
  86. char       *StrLast(char *s, char c);
  87. void        Tree(char *s);
  88. void        Usage(void);
  89.  
  90. void main(
  91.    int argc,
  92.    char *argv[]
  93. ){
  94.    char path[FILENAME_MAX] = "";
  95.    register char option = 'F';      /* Default opt for first arg: format */
  96.  
  97. /**
  98.  | Main program: allocates the internal buffer for the directory
  99.  | chain (pFIB); then scans the input line arguments; then calls
  100.  | Tree() that does the work.
  101. **/
  102.  
  103.    if ((pFIB = (struct FileInfoBlock *)
  104.             AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
  105.       NoMem();
  106.    }
  107.  
  108.    while (--argc > 0) {
  109.       switch ((*++argv)[0]) {
  110.          case '-':
  111.             switch (option = (*argv)[1]) {
  112.                case 'X':   case 'x':
  113.                   dirWanted = FALSE;
  114.                   break;
  115.                case 'N':
  116.                   caseSignificant = TRUE;
  117.                   namWanted = FALSE;
  118.                   break;
  119.                case 'n':
  120.                   caseSignificant = FALSE;
  121.                   namWanted = FALSE;
  122.                   break;
  123.                case 'D':   case 'd':
  124.                case 'F':   case 'f':
  125.                case 'R':   case 'r':
  126.                   break;
  127.                default:
  128.                   Usage();
  129.                   break;
  130.             }
  131.             break;
  132.          case '+':
  133.             switch (option = (*argv)[1]) {
  134.                case 'X':   case 'x':
  135.                   dirWanted = TRUE;
  136.                   break;
  137.                case 'N':
  138.                   caseSignificant = TRUE;
  139.                   namWanted = TRUE;
  140.                   break;
  141.                case 'n':
  142.                   caseSignificant = FALSE;
  143.                   namWanted = TRUE;
  144.                   break;
  145.                default:
  146.                   Usage();
  147.                   break;
  148.             }
  149.             break;
  150.          case '?':
  151.             Usage();
  152.             break;
  153.          default:
  154.             switch (option) {
  155.                case 'd':
  156.                   fullDirPath = FALSE;
  157.                   dirList = Add(*argv, dirList);
  158.                   break;
  159.                case 'D':
  160.                   fullDirPath = TRUE;
  161.                   dirList = Add(*argv, dirList);
  162.                   break;
  163.                case 'x':   case 'X':
  164.                   extList = Add(*argv, extList);
  165.                   break;
  166.                case 'f':   case 'F':
  167.                   mFormat = *argv;
  168.                   break;
  169.                case 'r':   case 'R':
  170.                   strcpy(path, *argv);
  171.                   option = '\0';
  172.                   break;
  173.                case 'N':   case 'n':
  174.                   namList = AddNam(*argv, namList);
  175.                   break;
  176.                default:             /* Cannot happen */
  177.                   break;
  178.             }
  179.             break;
  180.       }
  181.    }
  182.  
  183.    Tree(path);
  184.  
  185.    Cleanup(SYS_NORMAL_CODE);
  186. }
  187.  
  188. StringSet *Add(
  189.    char *s,
  190.    StringSet *pSS
  191. ){
  192.    register StringSet *t;
  193.  
  194. /**
  195.  | Adds the string "s" in the linked list with root "pSS".
  196. **/
  197.  
  198.    if ((t = malloc(sizeof(StringSet) + strlen(s))) == NULL)    NoMem();
  199.    t->next = pSS;
  200.    strcpy(t->what, s);
  201.  
  202.    return t;
  203. }
  204.  
  205. NamEntry *AddNam(
  206.    char *s,
  207.    NamEntry *pNE
  208. ){
  209.    register NamEntry *t;
  210.  
  211. /**
  212.  | Adds the name pattern "s" in the linked list with root "pNE".
  213.  | The pattern.library is opened at the first entry; and some memory
  214.  | for the new structure is obtained from the heap. If "s" is a real
  215.  | pattern, its "pArg" member is NULL and its "type" member is the
  216.  | PatternHandle (must be positive); if "s" is a simple string,
  217.  | "pArg" points to a cleaned copy of "s" and "type" is negative.
  218. **/
  219.  
  220.    if (PatternBase == NULL   &&
  221.     (PatternBase = OpenLibrary(PATLIB_NAME, PATLIB_MIN_VERSION)) == NULL) {
  222.       fprintf(stderr, "TREE: couldn't open %s rev. %d\n",
  223.              PATLIB_NAME, PATLIB_MIN_VERSION);
  224.       Cleanup(SYS_ABORT_CODE);
  225.    }
  226.  
  227.    if ((t = malloc(sizeof(NamEntry))) == NULL)  NoMem();
  228.  
  229.    if (IsPattern(s, outLine, 0)) {
  230.       t->pArg = NULL;
  231.       if ( (t->type = caseSignificant ?   AllocPattern(s, 0) :
  232.                                           AllocPatternNoCase(s, 0)) < 0) {
  233.          fprintf(stderr, "TREE: %s\n", PatternErrorString(t->type,
  234.                  "default", outLine, OUTLINE_DIM));
  235.          Cleanup(SYS_ABORT_CODE);
  236.       }
  237.    } else {
  238.       t->type = MATCH_STRING;
  239.       if ((t->pArg = malloc(strlen(outLine) + 1)) == NULL) {
  240.          free(t);
  241.          NoMem();
  242.       }
  243.       strcpy(t->pArg, outLine);
  244.    }
  245.  
  246.    t->next = pNE;
  247.    return t;
  248. }
  249.  
  250. void CheckControlC(void)
  251. {
  252.    if ((SetSignal(0L, SIGBREAKF_CTRL_C)) & SIGBREAKF_CTRL_C) {
  253.       aborted = TRUE;
  254.    }
  255. }
  256.  
  257. int CheckExt(
  258.    char *s
  259. ){
  260.    register char *pc;
  261.  
  262. /**
  263.  | Looks for the extension of the filename "s" (the characters after
  264.  | the last '.') and compares the extension with the linked list
  265.  | starting at "extList").
  266. **/
  267.  
  268.    return ((pc = StrLast(s, '.')) == NULL   ||   !Member(++pc, extList));
  269. }
  270.  
  271. void Cleanup(
  272.    int code
  273. ){
  274.    register StringSet *pS;
  275.    register NamEntry  *pNE;
  276.  
  277. /**
  278.  | Frees allocated memory, gives back all obtained resources.
  279. **/
  280.  
  281.    if (pFIB != NULL) FreeMem(pFIB, sizeof(struct FileInfoBlock));
  282.  
  283.    while ((pS = extList) != NULL) {
  284.       extList = pS->next;
  285.       free(pS);
  286.    }
  287.    while ((pS = dirList) != NULL) {
  288.       dirList = pS->next;
  289.       free(pS);
  290.    }
  291.    while ((pNE = namList) != NULL) {
  292.       namList = pNE->next;
  293.       if (pNE->pArg != NULL)  free(pNE->pArg);
  294.       if (pNE->type >  0)     FreePattern(pNE->type);
  295.       free(pNE);
  296.    }
  297.  
  298.    if (PatternBase != NULL)   CloseLibrary(PatternBase);
  299.  
  300.    exit(code);
  301. }
  302.  
  303. int CXBRK(void)
  304. {
  305.   aborted = TRUE;
  306.   return 0;
  307. }
  308.  
  309. int FileDate(
  310.    char *pc,
  311.    struct DateStamp *pDS
  312. ){
  313.    register int day, month, year;
  314.    static char *mName[] = {
  315.       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  316.       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  317.    };
  318.  
  319.    day    = pDS->ds_Days - 2251;
  320.    year   = (4 * day + 3) / 1461;
  321.    day   -= 1461 * year / 4;
  322.    year  += 1984;
  323.    month  = (5 * day + 2) / 153;
  324.    day    = day - (153 * month + 2) / 5 + 1;
  325.    month += 3;
  326.    if (month > 12) {
  327.       year++;
  328.       month -= 12;
  329.    }
  330.  
  331.    return sprintf(pc, "%02d-%s-%d", day, mName[--month], year);
  332. }
  333.  
  334. int FileTime(
  335.    char *pc,
  336.    struct DateStamp *pDS
  337. ){
  338.    register int hour, mins, sec;
  339.  
  340.    sec  = pDS->ds_Tick   / TICKS_PER_SECOND;
  341.    hour = pDS->ds_Minute / 60;
  342.    mins = pDS->ds_Minute % 60;
  343.  
  344.    return sprintf(pc, "%02d:%02d:%02d", hour, mins, sec);
  345. }
  346.  
  347. int Member(
  348.    char *s,
  349.    StringSet *pSS
  350. ){
  351.  
  352. /**
  353.  | Looks for the string "s" in the linked list with root "pSS".
  354.  | The comparison is case insensitive (stricmp).
  355. **/
  356.  
  357.    if (*s) {
  358.       while (pSS != NULL) {
  359.          if (!stricmp(s, pSS->what)) return TRUE;
  360.          pSS = pSS->next;
  361.       }
  362.    }
  363.  
  364.    return FALSE;
  365. }
  366.  
  367. int NamMatch(
  368.    char *name
  369. ){
  370.    register NamEntry *pNE = namList;
  371.  
  372. /**
  373.  | Compares the file name ("name") with the known name patterns.
  374. **/
  375.  
  376.    while (pNE != NULL) {
  377.       if (pNE->type == MATCH_STRING) {
  378.          if (caseSignificant) {
  379.             if (!strcmp(name, pNE->pArg))       return TRUE;
  380.          } else {
  381.             if (!stricmp(name, pNE->pArg))      return TRUE;
  382.          }
  383.       } else {
  384.          register LONG r = MatchThePattern(pNE->type, name);
  385.  
  386.          if (r < 0) {
  387.             fprintf(stderr, "TREE: %s\n",
  388.                     PatternErrorString(r, "default", outLine, OUTLINE_DIM));
  389.          }
  390.          if (r)   return TRUE;
  391.       }
  392.       pNE = pNE->next;
  393.    }
  394.  
  395.    return FALSE;
  396. }
  397.  
  398. void NoMem(void)
  399. {
  400.    fputs("TREE: couldn't obtain memory\n", stderr);
  401.    Cleanup(SYS_ABORT_CODE);
  402. }
  403.  
  404. void PrintFile(
  405.    char *s
  406. ){
  407.    register char *p = outLine;
  408.    register char *q = mFormat;
  409.  
  410. /**
  411.  | Prints the file name ("s") in the needed format.
  412. **/
  413.  
  414.    while (*q) {
  415.       if (*q == '%') {
  416.          switch (*++q) {
  417.             case 's':
  418.                {  register char *pc;
  419.  
  420.                   if ((pc = StrLast(s, '/')) != NULL   ||
  421.                       (pc = StrLast(s, ':')) != NULL) {
  422.                            strcpy(p, ++pc);
  423.                   } else {
  424.                      strcpy(p, s);
  425.                   }
  426.                   p += strlen(p);
  427.                   ++q;
  428.                }
  429.                break;
  430.             case 'p':
  431.                {  register char *pc;
  432.                   register char *ps=s;
  433.  
  434.                   if ((pc = StrLast(s, '/')) != NULL   ||
  435.                       (pc = StrLast(s, ':')) != NULL) {
  436.                            while (ps <= pc) *p++ = *ps++;
  437.                   }
  438.                }
  439.                ++q;
  440.                break;
  441.             case 'b':
  442.                {  register int i = sprintf(p, "%ld", pFIB->fib_Size);
  443.  
  444.                   p += i;
  445.                }
  446.                ++q;
  447.                break;
  448.             case 'd':
  449.                p += FileDate(p, &pFIB->fib_Date);
  450.                ++q;
  451.                break;
  452.             case 't':
  453.                p += FileTime(p, &pFIB->fib_Date);
  454.                ++q;
  455.                break;
  456.             case 'q':
  457.                *p++ = '\"';
  458.                ++q;
  459.                break;
  460.             default:
  461.                *p++ = *q++;
  462.                break;
  463.          }
  464.       } else {
  465.          *p++ = *q++;
  466.       }
  467.    }
  468.    *p = '\0';
  469.    puts(outLine);
  470. }
  471.  
  472. char *StrLast(
  473.    char *s,
  474.    char c
  475. ){
  476.    register char *pc = NULL;
  477.  
  478. /**
  479.  | Returns a pointer to the last occurrence of "c" in the
  480.  | string "s", or NULL.
  481. **/
  482.  
  483.    while (*s) {
  484.       if (*s == c) pc = s;
  485.       s++;
  486.    }
  487.  
  488.    return pc;
  489. }
  490.  
  491. void Tree(
  492.    char *path
  493. ){
  494.    BPTR dlock;
  495.    char fileName[FILENAME_MAX];
  496.    register char *pc, *pDir;
  497.    register DirEntry *rdE = NULL;
  498.    register DirEntry *pdE;
  499.  
  500. /**
  501.  | This procedure checks (recursively) a directory.
  502.  | "path" contains the full directory name; Tree() scans the wanted
  503.  | directory, processing immediately the 'true' files; the local
  504.  | subdirectories (if any) are checked recursively at the end.
  505.  | If an error is detected from Tree(), the directory/file test is
  506.  | stopped and the global flag "aborted" is set; this makes possible, in
  507.  | the further steps, to recursively free() all the memory that has been
  508.  | allocated in order to store subdirectory names.
  509.  |
  510.  | First: obtain a lock on the wanted directory, and Examine() the lock;
  511.  | since only one directory is being scanned at a time, we can use a
  512.  | single FileInfoBlock buffer.
  513. **/
  514.  
  515.    if ((dlock = Lock(path, ACCESS_READ)) == NULL) return;
  516.  
  517.    if (Examine(dlock, pFIB)) {
  518.  
  519. /**
  520.  | Prepare in "fileName" the full directory name - to which local
  521.  | filenames will be appended; and pDir, the pointer to the directory
  522.  | name according to the fullDirPath global flag.
  523. **/
  524.  
  525.       pc = strcpy(fileName, path);
  526.       pc += strlen(fileName);
  527.       if (pc != fileName   &&   *(pc-1) != ':')    *pc++ = '/';
  528.  
  529.       pDir = fullDirPath ? fileName : pc;
  530.  
  531. /**
  532.  | Now, loop over all directory entries. As already said, all the
  533.  | 'real' files are immediately checked; the subdirectory names are
  534.  | stored in a linked list to be examined at the end. This list is
  535.  | implemented as a LIFO tree (the simplest type).
  536. **/
  537.  
  538.       while (!aborted   &&   ExNext(dlock, pFIB)) {
  539.          (void) strcpy(pc, pFIB->fib_FileName);
  540.          if (pFIB->fib_DirEntryType < 0) {
  541.  
  542. /**
  543.  | A file. If a name pattern has been given, check this first;
  544.  | then check the extension, if any.
  545. **/
  546.  
  547.             if (namWanted ^ NamMatch(pc))    continue;
  548.             if (dirWanted ^ CheckExt(pc))    PrintFile(fileName);
  549.          } else {
  550.  
  551. /**
  552.  | If a memory allocation error is detected when asking space for
  553.  | our linked list, we exit the "while" loop; setting the "aborted"
  554.  | flag before exiting, ensures that all the memory we had from
  555.  | these malloc()'s will later be free()-ed recursively.
  556. **/
  557.  
  558.             if (!Member(pDir, dirList)) {
  559.                if ((pdE = malloc(sizeof(DirEntry) + strlen(fileName)))
  560.                      == NULL) {
  561.                   aborted = TRUE;
  562.                   break;
  563.                }
  564.  
  565.                (void) strcpy(pdE->name, fileName);
  566.                pdE->next = rdE;
  567.                rdE = pdE;
  568.             }
  569.          }
  570.  
  571.          CheckControlC();
  572.       }
  573.    }
  574.    UnLock(dlock);
  575.  
  576. /**
  577.  | Now, loop over all detected subdirectories (if any);
  578.  | freeing in the same time the memory used to store their names.
  579. **/
  580.  
  581.    while (rdE != NULL) {
  582.       if (!aborted) Tree(rdE->name);
  583.  
  584.       free(rdE->name);
  585.       pdE = rdE->next;
  586.       free(rdE);
  587.       rdE = pdE;
  588.    }
  589. }
  590.  
  591. void Usage(void) {
  592.  char *desc[] = {
  593.   "\nSyntax:\tTREE [opt arg [arg [ ... ]] [opt arg [arg [ ... ]] ...",
  594.   "Lists on stdout the file names in the directory tree. The options are:\n",
  595.   " -r name :    starting directory (default: current working directory).",
  596.   " -d name [name [ ... ]] :        name(s) (case not significant) of the",
  597.   "          directories to be excluded (directory name only, e.g. \"pk\").",
  598.   " -D name [name [ ... ]] :        name(s) (case not significant) of the",
  599.   "           directories to be excluded (full path, e.g. \"work:tex/pk\").",
  600.   " -n pat [pat [ ... ]] :  patterns matching files to be excluded; *OR*,",
  601.   " +n pat [pat [ ... ]] :         patterns matching the only files to be",
  602.   "           listed. If given uppercase (-N,+N) the case is significant.",
  603.   " -x ext [ext [ ... ]] :      extensions of files to be excluded; *OR*,",
  604.   " +x ext [ext [ ... ]] :     extensions of the only files to be listed.",
  605.   " -f \"fmt\" :     output format, between double quotes (\"). Can contain:",
  606.   "    %s - that will be substituted from the file name;",
  607.   "    %p - the file path (e.g. \"ram:\" or \"tex:pk/\");",
  608.   "    %b - the file size (in bytes);",
  609.   "    %d - the file date (e.g. \"08-May-1988\");",
  610.   "    %t - the file time (e.g. \"09:10:11\");",
  611.   "    %q - a double quote character (\");",
  612.   "    %% - the escape character % itself;",
  613.   "    all other characters will copied to stdout.\n",
  614.   NULL
  615.  };
  616.    register char **ppc = desc;
  617.  
  618.    while (*ppc) {
  619.       fputs(*ppc++, stderr);
  620.       putc('\n',    stderr);
  621.    }
  622.  
  623.    Cleanup(SYS_NORMAL_CODE);
  624. }
  625.